import java.util.ArrayList;
import java.util.Iterator;
import java.util.ListIterator;

/**
 * Classe que representa uma composição ferroviária.
 * Uma composição é um conjunto de locomotiva(s) e
 * vagões conectados para para fazer uma viagem.
 *
 */
public class Composicao {

	/**
	 * Variável usada para representar os carros desta composição
	 */
	private ArrayList<Carro> carros;

	/**
	 * Variável usada para armazenar o código
	 */
	private String codigo;

	/* TODO inicializar variáveis internas */
	public Composicao(String codigo) {
		this.codigo = codigo;
		carros = new ArrayList<Carro>();
	}

	/* TODO retornar tamanho da composição */
	public int tamanhoComposicao() {
		return carros.size();
	}

	/* TODO retornar carro na posição indicada */
	public Carro getCarro(int carro) {
		return this.carros.get(carro);
	}

	/* TODO adicionar um carro na composição (usar atributos "carros")
	 * - Se carro for locomotiva, inserir no início (posição 0) e dar um shift no restante do vetor
	 * - Senão, inserir no final
	 */
	public void adicionaCarro(Carro carro) {

		/***************************************************************************************************************************************************************************************/
		/**************************************************carro é do tipo Locomotiva**********************************************************************************************************/
		/***************************************************************************************************************************************************************************************/
		if(carro instanceof Locomotiva){
			///////////////////////////////////////////////////////////////////////////////////////Composição vazia/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			/////////////////////////////////////////////////////////////////////: locomotiva é o primeiro carro adicionado:///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			if(carros.isEmpty()) {
				//carros.ensureCapacity(carros.size());//capacidade é N-1, agora pode caber N
				carros.add(carro);//locomotiva na primeira posição
			}
			///////////////////////////////////////////////////////////////////////////////////////Composição não vazia/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			///////////////////////////////////////////////////// dar um shift em todos os carros da composição e adicionar a locomotiva na primeira posição////////////////////////////////////////////////////////////////////////////////////////
			/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			else{
				carros.add(carro);//somente para ganhar uma posição
				/*DESLOCAR OS DEMAIS VAGÕES: APENAS TROCA DAS REFERÊNCIAS*/
				/*MODIFICANDO UM ATRIBUTO*/
				//Nunca tem 1 carro apenas pois foi adicionado a locomotiva sempre tem 2 ou mais!
				for(int i = carros.size(); i >= 2; i--) java.util.Collections.swap(carros, i-1, i-2);//troca os elementos do arraylist anterior vai para posterior
			}//FIM DO TRATAMENTO COMPOSIÇÃO não vazia e carro a inserir é locomotiva
		}
		/****************************************************************************************************************************************************************************************/
		/*******************************************************carro é do tipo Vagão***********************************************************************************************************/
		/***************************************************************************************************************************************************************************************/
		if(carro instanceof VagaoLiquidos ||carro instanceof VagaoGraneleiro) carros.add(carro);//não possui locomotiva sempre na última posição
	}

	/* TODO retornar true se composição for válida, false caso contrário
	 * Composição é válida se possuir pelo menos uma locomotiva
	 */
	public boolean valida () {
		for(Carro i : carros){
			if(i instanceof Locomotiva) return true;
		}
		return false;	
	}

	/* TODO retornar soma das capacidades dos vagões de líquidos nesta composição */
	public double capacidadeTotalLiquidos() {
		double capacidadecomposicaoliquidos = 0.0;
		//referencia para realizar downcasting
		VagaoLiquidos j;
		for(Carro i : carros){
			if(i instanceof VagaoLiquidos){
				//downcasting
				j = (VagaoLiquidos)(i); 
				capacidadecomposicaoliquidos= capacidadecomposicaoliquidos+(double)(j.getCapacidade());
			}//fim do if
		}//fim do laço aprimorado
		return capacidadecomposicaoliquidos;	
	}

	/* TODO retornar soma das capacidades de vagões graneleiros nesta composição */
	public double capacidadeTotalGraneis() {
		double capacidadecomposicaograneis = 0.0;
		//referencia para realizar downcasting
		VagaoGraneleiro j;
		for(Carro i : carros){
			if(i instanceof VagaoGraneleiro){
				//downcasting
				j = (VagaoGraneleiro)(i); 
				capacidadecomposicaograneis= capacidadecomposicaograneis+(double)(j.getCapacidade());
			}//fim do if
		}//fim do laço aprimorado
		return capacidadecomposicaograneis;

	}

	/* TODO retornar true se esta composição é inflamável, false caso contrário
	 * Composição é inflamável se  existir pelo menos um vagão que possa
	 * carregar líquidos inflamáveis
	 */
	public boolean inflamavel () {
		boolean inflamabilidadecomposicao= false;
		//referencia para realizar downcasting
		VagaoLiquidos j;
		//laço aprimorado para percorrer todos os vagões da composição*/
		for(Carro i: carros){
			//analisa apenas os elementos que sejam do tipo VagaoLiquidos
			if(i instanceof VagaoLiquidos){
				j = (VagaoLiquidos)i;
				//se ocorrer um único carro do tipo que seja inflamável toda a composição será
				if(j.inflamavel()) inflamabilidadecomposicao = true;
			}
		}
		return inflamabilidadecomposicao;
	}

	/**
	 * Retorna a representação desta composição como String.
	 */
	public String toString() {
		String retorno = codigo + "|" + capacidadeTotalLiquidos() + "|" +
				capacidadeTotalGraneis() + "|";
		for (Carro carro : carros) {
			retorno = retorno + carro.getCodigo() + "|"; 
		}
		return retorno;
	}

	/**
	 * TODO carregar a composição com uma carga de graneis
	 * Como fazer:
	 *  - Percorrer os vagões a partir de 0 até o fim do vetor, e escolhe
	 *  o primeiro que puder carregar todo conteúdo
	 *  - Se não houver vagão que possa fazer isso, mas for possível dividir a
	 *  carga em vários vagões, então deve dividir a carga em vários vagões, deixando
	 *  somente o último incompleto. Por exemplo, se houver uma carga de 500 toneladas
	 *  e 3 vagões com capacidade de 200 toneladas, deve encher os dois primeiros vagões
	 *  e deixar 100 toneladas no terceiro. Os vagões vazios escolhidos na ordem em que aparecem
	 *  na composição.
	 *  
	 *  Retorno:
	 *   - Se não foi possível carregar o trem com a carga, null
	 *   - Caso for possível, retornar um vetor de tamanho N com os N vagões usados nesse
	 *   carregamento, na mesma ordem em que aparecem na composição 
	 */
	public Vagao[] carregarGraneis(int quantidade) {
		/*Se a capacidade da composição de carregar graneis for insuficiente retorna null*/
		final int vazio = 0;
		if((int)capacidadeTotalGraneis() < quantidade) return null;//testa se é possível transporta a carga nesta composição
		else{
			//Primeiro:
			//Procurar se existe um vagão com a capacidade total do carregamento desejado
			//se houver carregar nele
			//busca por este vagao unico
			for(Carro i: carros){				
				if(i instanceof VagaoGraneleiro){
					VagaoGraneleiro j = (VagaoGraneleiro)i;//i é do tipo Carro, preciso realizar downcasting
					//Procurando um vagão vazio que tenha como capacidade a quantidade a ser carregada
					if(j.getCapacidade() == quantidade && j.quantidadeCarregada() == vazio){
						//Encontrado o vagão e estava vazio
						VagaoGraneleiro[] vagaounico = new VagaoGraneleiro[1];//vetor de retorno contém apenas um carro
						if(j.carregarCarga(quantidade)){//modifica o atributo do vagaograneleiro onde foi inserido a carga
							vagaounico[0]= j;//passagem da referência do objeto
							return vagaounico;
						}
					}//fim do if do vaogaounico
				}//fim do if de checagem de tipo
			}//fim do laço para busca de um vagaounico para encaixar a quantidade desejada
			//CASO CONTRÁRIO: distribuir nos carros existentes
			//cria um arraylist com os vagoes que podem carregar pois não sabemos quantos vagoes entraram na composicao
			ArrayList<VagaoGraneleiro> vagoescarregados = new ArrayList<VagaoGraneleiro>();
			boolean preenchido = false;
			for(Carro i: carros){
				if(i instanceof VagaoGraneleiro){
					VagaoGraneleiro j =(VagaoGraneleiro)i;
					if(!j.carregado()){//vagão não esta cheio
						preenchido = j.carregarCarga(quantidade);
						vagoescarregados.add(j);
						if(preenchido) break;//sai do laço 
						else quantidade = quantidade - j.quantidadeCarregada();//atualiza a quantidade que falta a ser carregada
					}//fim do if de vagoes preenchidos com carga
				}
			}//fim do laço de busca por vagões que podem ser preenchidos
			//Converter o ArrayList em um vetor e retornar
			VagaoGraneleiro[] vetorvagoescarregados = vagoescarregados.toArray(new VagaoGraneleiro[vagoescarregados.size()]);
			return vetorvagoescarregados;
		}
	}
	/**
	 * TODO similar ao carregarGraneis, porém só deve usar vagões que aceitam o
	 * tipo de carga informada. DEVE alterar o tipo de carga sendo usada pelo vagão
	 * SOMENTE ao carregá-lo.
	 * 
	 * Dica: use Vagao.aceitaConteudo() para checar se o vagão pode receber o tipo de carga 
	 */
	public Vagao[] carregarLiquido(int quantidade, ConteudoLiquido conteudo) {
		/*Se a capacidade da composição de carregar graneis for insuficiente retorna null*/
		final int vazio = 0;
		if((int)capacidadeTotalLiquidos() < quantidade) return null;//testa se é possível transporta a carga nesta composição
		else{
			//Primeiro:
			//Procurar se existe um vagão com a capacidade total do carregamento desejado
			//se houver carregar nele
			//busca por este vagao unico
			for(Carro i: carros){				
				if(i instanceof VagaoLiquidos){
					VagaoLiquidos j = (VagaoLiquidos)i;//i é do tipo Carro, preciso realizar downcasting
					//Procurando um vagão vazio que tenha como capacidade a quantidade a ser carregada e aceita este tipo de conteudo
					if(j.getCapacidade() == quantidade && j.quantidadeCarregada() == vazio && j.aceitaConteudo(conteudo)){
						//Encontrado o vagão e estava vazio
						VagaoLiquidos[] vagaounico = new VagaoLiquidos[1];//vetor de retorno contém apenas um carro
						if(j.carregarCarga(quantidade)){//modifica o atributo do vagaograneleiro onde foi inserido a carga
							j.setConteudoUsado(conteudo);//PARTE DIFERENTE
							vagaounico[0]= j;//passagem da referência do objeto
							return vagaounico;
						}
					}//fim do if do vaogaounico
				}//fim do if de checagem de tipo
			}//fim do laço para busca de um vagaounico para encaixar a quantidade desejada
			//CASO CONTRÁRIO: distribuir nos carros existentes
			//cria um arraylist com os vagoes que podem carregar pois não sabemos quantos vagoes entraram na composicao
			ArrayList<VagaoLiquidos> vagoescarregados = new ArrayList<VagaoLiquidos>();
			boolean preenchido = false;
			for(Carro i: carros){
				if(i instanceof VagaoLiquidos){
					VagaoLiquidos j =(VagaoLiquidos)i;
					if(!j.carregado() && j.aceitaConteudo(conteudo)){//vagão não esta cheio
						preenchido = j.carregarCarga(quantidade);
						j.setConteudoUsado(conteudo);//caso um vagao que foi preenchido estava vazio precisa setar o conteudo dele
						vagoescarregados.add(j);
						if(preenchido) break;//sai do laço 
						else quantidade = quantidade - j.quantidadeCarregada();//atualiza a quantidade que falta a ser carregada
					}//fim do if de vagoes preenchidos com carga
				}
			}//fim do laço de busca por vagões que podem ser preenchidos
			//Converter o ArrayList em um vetor e retornar
			VagaoLiquidos[] vetorvagoescarregados = vagoescarregados.toArray(new VagaoLiquidos[vagoescarregados.size()]);
			return vetorvagoescarregados;
		}

	}

	/**
	 * TODO remove o carro indicado da composição
	 */
	public void removeCarro(Carro carro) {
		carros.remove(carro);
	}
}
